//
// Created by alexa on 5/13/2024.
//

#include "UnitRepository.h"
#include "../../entities/combat-unit/launcher/Launcher.h"
#include "../../entities/combat-unit/self-propelled-launcher/SelfPropelledLauncher.h"

#include <fstream>

#include <nlohmann/json.hpp>

using json = nlohmann::json;

CombatUnit *UnitRepository::findById(string id) {
    return source.getById(id);
}

vector<CombatUnit *> UnitRepository::findAll() {
    return source.getAll();
}

void UnitRepository::deleteOne(CombatUnit *unit) {
    if (source.getById(unit->getId())) {
        source.removeById(unit->getId());
    }
    if (!fileSourcePath.empty()) {
        saveDataToFile();
    }
}

void UnitRepository::save(CombatUnit *unit) {
    if (!source.getById(unit->getId())) source.add(unit);
    if (!fileSourcePath.empty()) {
        saveDataToFile();
    }
}

void UnitRepository::loadDataFromFile() {
    if (fileSourcePath.empty()) return;
    ifstream file(fileSourcePath);
    if (!file.good()) {
        file.close();
        return;
    }
    json data;
    try {
        data = json::parse(file);
    } catch (json::exception &e) {
        file.close();
        return;
    }
    if (!data.is_array()) return;
    for (const auto &el: data) {
        try {
            string id = el.at("id");
            string type = el.at("type");
            string name = el.at("name");
            int railsCount = el.at("railsCount");
            float shotsDelay = el.at("shotsDelay");
            int missilesCount = el.at("missilesCount");
            float aimX = el.at("aimX");
            float aimY = el.at("aimY");
            string crewId = el.at("crewId");
            string missileId = el.at("missileId");
            float posX = el.at("posX");
            float posY = el.at("posY");
            vector<Specialization> requiredSpecializations;
            for (const auto &spec: el.at("requiredSpecializations")) {
                if (!spec.is_number_integer()) continue;
                requiredSpecializations.push_back(static_cast<Specialization>(spec));
            }
            Launcher *unit;
            if (type == "selfPropelledLauncher") {
                float length = el.at("length");
                float width = el.at("width");
                float maxSpeed = el.at("maxSpeed");
                float powerReserve = el.at("powerReserve");
                float remainingPowerReserve = el.at("remainingPowerReserve");
                float preparingTime = el.at("preparingTime");
                float weight = el.at("weight");
                SelfPropelledLauncher *launcher = new SelfPropelledLauncher(name, railsCount, shotsDelay, length, width,
                                                                            maxSpeed, weight, powerReserve,
                                                                            preparingTime);
                launcher->setRemainingPowerReserve(remainingPowerReserve);
                unit = launcher;
            } else {
                Launcher *launcher = new Launcher(name, railsCount, shotsDelay);
                unit = launcher;
            }
            unit->setId(id);
            unit->setMissilesCount(missilesCount);
            unit->setAimX(aimX);
            unit->setAimY(aimY);
            unit->setPosX(posX);
            unit->setPosY(posY);
            unit->setMissileId(missileId);
            unit->setCrewId(crewId);
            unit->setRequiredSpecializations(requiredSpecializations);
            source.add(unit);
        } catch (json::exception &e) {
            continue;
        }
    }
    file.close();
}

vector<int> specsToInt(vector<Specialization> specs) {
    vector<int> res;
    for (const auto &spec: specs) {
        res.push_back((int) spec);
    }
    return res;
}

void UnitRepository::saveDataToFile() {
    ofstream file(fileSourcePath);

    json jarr = json::array();
    vector<CombatUnit *> list = findAll();

    for (const auto &el: list) {
        json jobj = json::object();
        Launcher *launcher = dynamic_cast<Launcher *>(el);
        jobj["id"] = launcher->getId();
        jobj["name"] = launcher->getName();
        jobj["crewId"] = launcher->getCrewId();
        jobj["missileId"] = launcher->getMissileId();
        jobj["posX"] = launcher->getPosX();
        jobj["posY"] = launcher->getPosY();
        jobj["requiredSpecializations"] = specsToInt(launcher->getRequiredSpecializations());
        jobj["railsCount"] = launcher->getRailsCount();
        jobj["shotsDelay"] = launcher->getShotsDelay();
        jobj["missilesCount"] = launcher->getMissilesCount();
        jobj["aimX"] = launcher->getAimX();
        jobj["aimY"] = launcher->getAimY();
        SelfPropelledLauncher *selfPropelledLauncher = dynamic_cast<SelfPropelledLauncher *>(el);
        if (selfPropelledLauncher != nullptr) {
            jobj["length"] = selfPropelledLauncher->getLength();
            jobj["width"] = selfPropelledLauncher->getWidth();
            jobj["maxSpeed"] = selfPropelledLauncher->getMaxSpeed();
            jobj["powerReserve"] = selfPropelledLauncher->getPowerReserve();
            jobj["remainingPowerReserve"] = selfPropelledLauncher->getRemainingPowerReserve();
            jobj["preparingTime"] = selfPropelledLauncher->getPreparingTime();
            jobj["weight"] = selfPropelledLauncher->getWeight();
            jobj["type"] = "selfPropelledLauncher";
        } else {
            jobj["type"] = "launcher";
        }
        jarr.push_back(jobj);
    }
    file << jarr.dump(4) << endl;
    file.close();
}

UnitRepository::UnitRepository(const string &fileSourcePath) : fileSourcePath(fileSourcePath) {
    if (!fileSourcePath.empty()) {
        loadDataFromFile();
    }
}

const string &UnitRepository::getFileSourcePath() const {
    return fileSourcePath;
}

void UnitRepository::setFileSourcePath(const string &fileSourcePath) {
    this->fileSourcePath = fileSourcePath;
    if (!fileSourcePath.empty()) {
        loadDataFromFile();
    }
}

UnitRepository::~UnitRepository() {
    vector<CombatUnit *> list = findAll();
    for (auto &el: list) {
        delete el;
    }
}
